#include "wm_efuse.h"

#define SIGNATURE_WORD (0xA0FFFF9FUL)
#define FT_MAGICNUM_ADDR (0x8000000UL)
#define FT_PARAM_RUNTIME_ADDR	(FT_MAGICNUM_ADDR + 0x1000)
#define FT_PARAM_EXT_REVERSED_LEN 32
#define CMD_ADC_CAL 1


typedef struct
{
	uint32_t magic_no;
	uint32_t checksum;
	uint8_t  rsv1[6];
	uint16_t version_no;
	uint8_t  rsv2[6];
	uint16_t ext_param_len;
	uint32_t rsv3;
	uint32_t rsv4;
	uint32_t rsv5;
	uint32_t rsv6;
	uint32_t rsv7;
	uint32_t rsv8;
	uint8_t  rsv9[84];
} FT_ParamTypeDef;

typedef struct
{
	uint32_t rsv1;
	uint32_t rsv2;
	Ft_AdcTypeDef adc_cal_param;
	Ft_TempTypeDef temp_cal_param;
} FT_ParamExt1TypeDef;

typedef struct
{
	FT_ParamTypeDef ft_param;
	uint32_t ext_checksum;
	FT_ParamExt1TypeDef ft_ext1;
	uint8_t rsv[FT_PARAM_EXT_REVERSED_LEN];
} FT_ParamVer1TypeDef;

FT_ParamVer1TypeDef gFtParamVer1;
FT_ParamTypeDef *gFtParam = (FT_ParamTypeDef *)&gFtParamVer1;

static int _ft_param_init(uint32_t ft_addr, FT_ParamTypeDef *pft);

int Hal_Efuse_Param_init(void)
{
	int ret = 0;
	FT_ParamTypeDef *pft = NULL;
	
	if (gFtParam->magic_no == SIGNATURE_WORD)
	{
		return HAL_OK;
	}
	
	pft = malloc(sizeof(FT_ParamVer1TypeDef));
	if (pft == NULL)
	{
		return HAL_ERROR;
	}
	
	memset(pft, 0xFF, sizeof(FT_ParamVer1TypeDef));
	memset(gFtParam, 0xFF, sizeof(FT_ParamVer1TypeDef));
	ret = _ft_param_init(FT_PARAM_RUNTIME_ADDR, pft);
	if (ret == 0)
	{
		memset(pft, 0xFF, sizeof(FT_ParamVer1TypeDef));
		ret = _ft_param_init(FT_MAGICNUM_ADDR, pft);
	}
	if (ret == 1)
	{
		memcpy((uint8_t *)gFtParam, (uint8_t *)pft, sizeof(FT_ParamVer1TypeDef));
	}
	else if(ret == 2)
	{
		memcpy((uint8_t *)gFtParam, (uint8_t *)pft, sizeof(FT_ParamTypeDef));
	}
	
	free(pft);
	
	return HAL_OK;
}

static int _ft_ext1_valid(FT_ParamTypeDef *pft)
{
	if ((pft->version_no > 0) && (pft->version_no < 0xFFFF) && (pft->ext_param_len >= sizeof(FT_ParamExt1TypeDef)) &&
				(pft->ext_param_len <= (sizeof(FT_ParamExt1TypeDef) + FT_PARAM_EXT_REVERSED_LEN)))
	{
		return 1;
	}
	return 0;
}

static int _ft_param_init(uint32_t ft_addr, FT_ParamTypeDef *pft)
{
	uint32_t crcvalue = 0;
	CRC_HandleTypeDef ctx;
	FT_ParamVer1TypeDef *pft_ver1 = NULL;
	
	HAL_FLASH_Read(ft_addr, (uint8_t *)pft, sizeof(FT_ParamVer1TypeDef));
	if (pft->magic_no == SIGNATURE_WORD)
	{
		HAL_CRYPTO_CRC_Init(&ctx, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, (OUTPUT_REFLECT | INPUT_REFLECT));
		HAL_CRYPTO_CRC_Update(&ctx, (uint8_t *)pft + 8, sizeof(FT_ParamTypeDef) - 8);
		HAL_CRYPTO_CRC_Final(&ctx, &crcvalue);
		if (pft->checksum != crcvalue)
		{
			return 0;
		}
		if (_ft_ext1_valid(pft))
		{
			pft_ver1 = (FT_ParamVer1TypeDef *)pft;
			HAL_CRYPTO_CRC_Init(&ctx, 0xFFFFFFFF, CRYPTO_CRC_TYPE_32, (OUTPUT_REFLECT | INPUT_REFLECT));
			HAL_CRYPTO_CRC_Update(&ctx, (uint8_t *)&(pft_ver1->ft_ext1), pft->ext_param_len);
			HAL_CRYPTO_CRC_Final(&ctx, &crcvalue);
			if (pft_ver1->ext_checksum == crcvalue)
			{
				return 1;
			}
			else
			{
				return 0;
			}
		}
		pft->version_no = 0xFFFF;
		pft->ext_param_len = 0xFFFF;
		
		return 2;
	}
	return 0;
}

static int HAL_Efuse_Param_Get(uint32_t cmd, void *data, uint32_t len)
{
	switch (cmd)
	{
		case CMD_ADC_CAL:
			if (_ft_ext1_valid(gFtParam))
			{
				memcpy(data, (uint8_t *)&gFtParamVer1.ft_ext1.adc_cal_param, len);
			}
			else
			{
				return HAL_ERROR;
			}
			break;
			
		default:
			return HAL_ERROR;
	}
	
	return HAL_OK;
}

int HAL_Efuse_Get_ADC_Param(Ft_AdcTypeDef *adc_param)
{
	Hal_Efuse_Param_init();
	return HAL_Efuse_Param_Get(CMD_ADC_CAL, (void *)adc_param, sizeof(Ft_AdcTypeDef));
}